/**
  **************************************************************************
  * @file     main.c
  * @version  v2.0.3
  * @date     2021-12-17
  * @brief    main program
  **************************************************************************
  *                       Copyright notice & Disclaimer
  *
  * The software Board Support Package (BSP) that is made available to
  * download from Artery official website is the copyrighted work of Artery.
  * Artery authorizes customers to use, copy, and distribute the BSP
  * software and its related documentation for the purpose of design and
  * development in conjunction with Artery microcontrollers. Use of the
  * software is governed by this copyright notice and the following disclaimer.
  *
  * THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES,
  * GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS,
  * TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR
  * STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS,
  * INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
  * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
  *
  **************************************************************************
  */

#include "bsp.h"
#include "usb_conf.h"
#include "usb_core.h"
#include "usbh_int.h"
#include "usbh_user.h"
#include "usbh_msc_class.h"
#include "ff.h"
#include "os_obj.h"

/* usb global struct define */
otg_core_type otg_core_struct;
void usb_clock48m_select(usb_clk48_s clk_s);
void usb_gpio_config(void);
void usb_low_power_wakeup_config(void);

/**
  * @brief  usb_task
  * @param  argument
  * @retval none
  */
void usb_task(void const *argument)
{
	uint32_t work_tm_cnt;
	
	// nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);

	/* usb gpio config */
	usb_gpio_config();

#ifdef USB_LOW_POWER_WAKUP
	usb_low_power_wakeup_config();
#endif

	/* enable otgfs clock */
	crm_periph_clock_enable(OTG_CLOCK, TRUE);

	/* select usb 48m clcok source */
	usb_clock48m_select(USB_CLK_HEXT);

	/* enable otgfs irq */
	nvic_irq_enable(OTG_IRQ, 0, 0);

	/* init usb */
	usbh_init(&otg_core_struct,
			  USB_FULL_SPEED_CORE_ID,
			  USB_ID,
			  &uhost_class_handler,
			  &usbh_user_handle);
	while (1)
	{
		usbh_loop_handler(&otg_core_struct.host);
		osDelay(1);
		
		// global_state = 7 dicate user app mode
		if(otg_core_struct.host.global_state)
		{
			work_tm_cnt++;
			if(work_tm_cnt > 50 * OS_TICK_FREQ/10)
			{
				if(otg_core_struct.host.global_state < 7)
				{
					/* init usb */
					usbh_init(&otg_core_struct,
							  USB_FULL_SPEED_CORE_ID,
							  USB_ID,
							  &uhost_class_handler,
							  &usbh_user_handle);
				}
			}
		}
		else
		{
			work_tm_cnt = 0;
		}
	}
}

/**
  * @brief  usb 48M clock select
  * @param  clk_s:USB_CLK_HICK, USB_CLK_HEXT
  * @retval none
  */
void usb_clock48m_select(usb_clk48_s clk_s)
{
	if (clk_s == USB_CLK_HICK)
	{
		crm_usb_clock_source_select(CRM_USB_CLOCK_SOURCE_HICK);

		/* enable the acc calibration ready interrupt */
		crm_periph_clock_enable(CRM_ACC_PERIPH_CLOCK, TRUE);

		/* update the c1\c2\c3 value */
		acc_write_c1(7980);
		acc_write_c2(8000);
		acc_write_c3(8020);

		/* open acc calibration */
		acc_calibration_mode_enable(ACC_CAL_HICKTRIM, TRUE);
	}
	else
	{
		switch (system_core_clock)
		{
		/* 48MHz */
		case 48000000:
			crm_usb_clock_div_set(CRM_USB_DIV_1);
			break;

		/* 72MHz */
		case 72000000:
			crm_usb_clock_div_set(CRM_USB_DIV_1_5);
			break;

		/* 96MHz */
		case 96000000:
			crm_usb_clock_div_set(CRM_USB_DIV_2);
			break;

		/* 120MHz */
		case 120000000:
			crm_usb_clock_div_set(CRM_USB_DIV_2_5);
			break;

		/* 144MHz */
		case 144000000:
			crm_usb_clock_div_set(CRM_USB_DIV_3);
			break;

		/* 168MHz */
		case 168000000:
			crm_usb_clock_div_set(CRM_USB_DIV_3_5);
			break;

		/* 192MHz */
		case 192000000:
			crm_usb_clock_div_set(CRM_USB_DIV_4);
			break;

		/* 216MHz */
		case 216000000:
			crm_usb_clock_div_set(CRM_USB_DIV_4_5);
			break;

		/* 240MHz */
		case 240000000:
			crm_usb_clock_div_set(CRM_USB_DIV_5);
			break;

		/* 264MHz */
		case 264000000:
			crm_usb_clock_div_set(CRM_USB_DIV_5_5);
			break;

		/* 288MHz */
		case 288000000:
			crm_usb_clock_div_set(CRM_USB_DIV_6);
			break;

		default:
			break;
		}
	}
}

/**
  * @brief  this function config gpio.
  * @param  none
  * @retval none
  */
void usb_gpio_config(void)
{
	gpio_init_type gpio_init_struct;

	crm_periph_clock_enable(OTG_PIN_GPIO_CLOCK, TRUE);
	gpio_default_para_init(&gpio_init_struct);

	gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
	gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
	gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
	gpio_init_struct.gpio_pull = GPIO_PULL_NONE;

	/* dp and dm */
	gpio_init_struct.gpio_pins = OTG_PIN_DP | OTG_PIN_DM;
	gpio_init(OTG_PIN_GPIO, &gpio_init_struct);

	gpio_pin_mux_config(OTG_PIN_GPIO, OTG_PIN_DP_SOURCE, OTG_PIN_MUX);
	gpio_pin_mux_config(OTG_PIN_GPIO, OTG_PIN_DM_SOURCE, OTG_PIN_MUX);

#ifdef USB_SOF_OUTPUT_ENABLE
	crm_periph_clock_enable(OTG_PIN_SOF_GPIO_CLOCK, TRUE);
	gpio_init_struct.gpio_pins = OTG_PIN_SOF;
	gpio_init(OTG_PIN_SOF_GPIO, &gpio_init_struct);
	gpio_pin_mux_config(OTG_PIN_GPIO, OTG_PIN_SOF_SOURCE, OTG_PIN_MUX);
#endif

	/* otgfs use vbus pin */
#ifndef USB_VBUS_IGNORE
	gpio_init_struct.gpio_pins = OTG_PIN_VBUS;
	gpio_init_struct.gpio_pull = GPIO_PULL_DOWN;
	gpio_pin_mux_config(OTG_PIN_GPIO, OTG_PIN_VBUS_SOURCE, OTG_PIN_MUX);
	gpio_init(OTG_PIN_GPIO, &gpio_init_struct);
#endif

#ifdef USBH_5V_POWER_SWITCH
	crm_periph_clock_enable(OTG_PIN_POWER_SWITCH_CLOCK, TRUE);
	gpio_bits_set(OTG_PIN_POWER_SWITCH_GPIO, OTG_PIN_POWER_SWITCH);

	gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
	gpio_init_struct.gpio_out_type = GPIO_OUTPUT_OPEN_DRAIN;
	gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;
	gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
	gpio_init_struct.gpio_pins = OTG_PIN_POWER_SWITCH;
	gpio_init(OTG_PIN_POWER_SWITCH_GPIO, &gpio_init_struct);
#endif
}
#ifdef USB_LOW_POWER_WAKUP
/**
  * @brief  usb low power wakeup interrupt config
  * @param  none
  * @retval none
  */
void usb_low_power_wakeup_config(void)
{
	exint_init_type exint_init_struct;

	crm_periph_clock_enable(CRM_SCFG_PERIPH_CLOCK, TRUE);
	exint_default_para_init(&exint_init_struct);

	exint_init_struct.line_enable = TRUE;
	exint_init_struct.line_mode = EXINT_LINE_INTERRUPUT;
	exint_init_struct.line_select = OTG_WKUP_EXINT_LINE;
	exint_init_struct.line_polarity = EXINT_TRIGGER_RISING_EDGE;
	exint_init(&exint_init_struct);

	nvic_irq_enable(OTG_WKUP_IRQ, 0, 0);
}

/**
  * @brief  this function handles otgfs wakup interrupt.
  * @param  none
  * @retval none
  */
void OTG_WKUP_HANDLER(void)
{
	exint_flag_clear(OTG_WKUP_EXINT_LINE);
}

#endif

/**
  * @brief  this function handles otgfs interrupt.
  * @param  none
  * @retval none
  */
void OTG_IRQ_HANDLER(void)
{
	usbh_irq_handler(&otg_core_struct);
}

/**
  * @brief  usb delay millisecond function.
  * @param  ms: number of millisecond delay
  * @retval none
  */
#include "rtx_lib.h"
void usb_delay_ms(uint32_t ms)
{
	/* user can define self delay function */
	if (IsException() || IsIrqMasked())
	{
		bsp_delay_ms(ms);
	}
	else
	{
		osDelay(ms_ticks(ms)+1);
	}
}

/**
  * @brief  usb delay microsecond function.
  * @param  us: number of microsecond delay
  * @retval none
  */
void usb_delay_us(uint32_t us)
{
	bsp_delay_us(us);
}

/**
  * @}
  */

/**
  * @}
  */
